#pragma once
#include <jsoncpp/json/json.h>
#include<vector>
#include<cassert>
#include<mutex>
#include <algorithm>//
#include<fstream>
#include<iostream>
#include<string>

#include"../comm/httplib.h"
#include"../comm/spdlog.hpp"
#include"../comm/util.hpp"
#include"oj_view.hpp"
#include"oj_model.hpp"


namespace y_control
{
    struct Machine
    {
    public:
        std::string ip;//编译服务ip
        int port;//编译服务端口
        uint64_t load;//编译服务的负载
        std::mutex *mtx;//mutex禁止拷贝，使用指针
        
        Machine():ip(""),port(0),load(0),mtx(nullptr){}
        ~Machine()
        {}

        //提升负载
        void IncLoad()
        {
            if(mtx) mtx->lock();
            ++load;
            if(mtx) mtx->unlock();
        }

        //减少负载
        void DecLoad()
        {
            if(mtx) mtx->lock();
            --load;
            if(mtx) mtx->unlock();
        }

        void ResetLoad()
        {
            if(mtx) mtx->lock();
            load=0;
            if(mtx) mtx->unlock();
        }

        uint64_t Load()
        {
            uint64_t _load = 0;
            if (mtx) mtx->lock();
            _load = load;
            if (mtx) mtx->unlock();
            return _load;
        }
    };

    const std::string service_machine="./conf/service_machine.conf";
    //负载均衡模块
    class LoadBlance
    {
    public:
        LoadBlance()
        {
            assert(LoadConf(service_machine));
            INFO("加载{}成功",service_machine);
        }
        ~LoadBlance(){}

        //智能选择主机进行业务
        bool SmartChoice(int* id,Machine** m)
        {
            mtx.lock();
            int online_num=on_line.size();
            if(online_num==0)
            {
                mtx.unlock();//
                FATAL("所有后端编译主机已经下线");
                return false;
            }
            *id =on_line[0];
            *m=&machines[on_line[0]];
            uint64_t min_load=machines[on_line[0]].Load();
            for(int i=1;i<online_num;i++)
            {
                uint64_t curr_load=machines[on_line[i]].Load();
                if(min_load>curr_load)
                {
                    min_load=curr_load;
                    *id=on_line[i];
                    *m=&machines[on_line[i]];
                }
            }
            mtx.unlock();
            return true;
        }

        //下线主机
        void OfflineMachine(int which)
        {
            mtx.lock();
            for(auto iter = on_line.begin(); iter != on_line.end(); iter++)
            {
                if(*iter == which)
                {
                    machines[which].ResetLoad();
                    //要离线的主机已经找到啦
                    on_line.erase(iter);
                    off_line.push_back(which);
                    break; //因为break的存在，所有我们暂时不考虑迭代器失效的问题
                }
            }
            mtx.unlock();
        }

        void OnlineMachine()
        {
            //我们统一上线，后面统一解决
            mtx.lock();
            on_line.insert(on_line.end(), off_line.begin(), off_line.end());
            off_line.erase(off_line.begin(), off_line.end());
            mtx.unlock();

            INFO("所有的主机有上线啦!");
        }

        private:
        bool LoadConf(const std::string& machine_conf)
        {
            std::ifstream in(machine_conf);
            if(!in.is_open())
            {
                FATAL("加载{}失败",machine_conf);
                return false;
            }
            
            //加载主机
            std::string line;
            while(std::getline(in,line))
            {
                std::vector<std::string> tokens;
                y_util::StringUtil::SqlitString(line,&tokens,":");
                if(tokens.size()!=2)
                {
                    WARN("切分主机信息{}失败",line);
                    continue;
                }
                Machine m;
                m.ip=tokens[0];
                m.port=std::atoi(tokens[1].c_str());
                m.load=0;
                m.mtx=new std::mutex();

                on_line.push_back(machines.size());
                machines.push_back(m);
            }
            in.close();
            return true;
        }
        private:
        std::vector<Machine> machines;//主机对应下标
        std::vector<int> on_line;//在线主机id
        std::vector<int> off_line;//离线主机id
        std::mutex mtx;//保证数据安全
    };
    
    class Control
    {
    public:
        Control(){}
        ~Control(){}

        void RecoveryMachine()
        {
            _load_blance.OnlineMachine();
        }

        bool AllQueations(std::string* html)
        {

            std::vector<y_model::Question> all;
            if(_model.GetAllQuestions(all))
            {
                std::sort(all.begin(),all.end(),[](const y_model::Question& q1,const y_model::Question& q2)
                {
                    return std::atoi(q1.number.c_str())<std::atoi(q2.number.c_str());
                });
                _view.AllExpandHtml(all,html);
                return true;
            }
            else *html="获取题目失败，形成题目列表失败";
            return false;
        }

        bool Question(const std::string& number,std::string* html)
        {
            y_model::Question q;
            if(_model.GetOneQuestion(number,q))
            {
                _view.OneExpandHtml(q,html);
                return true;
            }
            else *html="指定题目"+number+"不存在";
            return false;
        }
         /****************************************************
         * 参数：
         * in_json:{"code":用户提交的代码,
         * "input":用户给自己提交的代码的输入，不做任何处理,
         * "cpu_limit":时间要求,
         * "mem_limit":空间要求}
         *
         * out_json:{"status":状态码,
         * "reason":请求结果,
         * //选填
         * "stdout":程序运行完结果,
         * "stderr":程序运行完错误结果}
         ****************************************************/
        void Judge(const std::string& number,const std::string in_json,std::string* out_json)
        {
            //获取题目细节
            y_model::Question q;
            _model.GetOneQuestion(number,q);

            //in_json进行反序列化，得到题目id,得到用户提交的源代码,input
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json,in_value);

            //重新拼接用户代码+测试用例代码，形成新代码
            Json::Value compile_value;
            compile_value["input"]=in_value["input"].asString();
            std::string code =in_value["code"].asString();
            compile_value["code"]=code+"\n"+q.tail;//
            compile_value["cpu_limit"]=q.cpu_limit;
            compile_value["mem_limit"]=q.mem_limit;
            Json::FastWriter writer;
            std::string compile_string=writer.write(compile_value);

            // 3. 选择负载最低的主机(差错处理)
            // 规则: 一直选择，直到主机可用，否则，就是全部挂掉
            while(true)
            {
                int id=0;
                Machine *m=nullptr;
                if(!_load_blance.SmartChoice(&id,&m))
                {
                    FATAL("主机全部挂掉，不可用");
                    break;
                }

                //发起http请求。得到结果
                httplib::Client cli(m->ip,m->port);
                m->IncLoad();
                INFO("选择主机成功，主机id:{}->详情: 主机:ip{},端口号:{},当前主机负载为:{}",id,m->ip,m->port,m->Load());
                if(auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
                {
                    if(res->status==200)
                    {
                        *out_json=res->body;
                        m->DecLoad();
                        INFO("请求编译和运行服务成功");
                        break;
                    }
                    m->DecLoad();
                }
                else
                {
                    ERROR("当前请求主机id:{}->详情:主机:ip{},端口号:{},可能已经下线了",id,m->ip,m->port);
                    _load_blance.OfflineMachine(id);
                }
            }
        }
    private:
        y_model::Model _model;//提供后台数据
        y_view::View _view;//提供html渲染功能
        LoadBlance _load_blance;//核心负载均衡器
    };

}